import { createLog } from './log.class'
import { Create } from './create.class'
import { classMap, maskTypeList,fontTypeList } from './map-data'
import { promiseGetImage } from './get-image.promise'
import Figure from './figure.class'

const Log = createLog(false) // 可以达到是否进行打印的效果！

class CutImage {
  constructor(config) {
    this.CONFIG = {}

    this.$DOM = {}

    this.ZOOM = 1
    this.MASK_SAFE_ZOOM = 0.6
    this.IS_DRAG = false

    this.$DATA = {
      // 表单数据
      formData: {
        type: 'circle',
        scale: '2.6:1',
        point: 5,
        rotate: 0,
        code: '年',
        font: '宋体'
      },

      offsetSize: {
        x: 0,
        y: 0,
      },

      // 视图图片数据
      viewSize: {
        x: 0,
        y: 0,
        w: 0,
        h: 0,
      },

      // 图片真实大小
      realSize: {
        width: 0,
        height: 0,
      },

      // 裁剪图形对应参数
      maskArgs: {},
    }

    // 拖拽之前的备份数据
    this.copyLastViewSize = {}

    this.mergeConfig(config)
    this.init()
  }

  // 事务总线 --

  // 初始化 - 合并初始配置
  mergeConfig(config) {
    Log._info('节点', '合并初始化配置 mergeConfig')
    if (!config.id) {
      Log._err('没有id，啥也干不了哦！')
      return false
    }

    const baseConfig = {
      wheelStep: 0.02,
    }

    this.CONFIG = Object.assign({}, baseConfig, config)
    this.$wrap = document.getElementById(config.id)
  }

  // 初始化 - 初始化业务需求
  init() {
    // 初始化页面结构
    this.initElement()

    this.$DATA.cutData = this.countCutData()

    this.moduleImageData = this.countModuleImageData()

    this.initPreviewCanvas()
    // 初始化裁剪的 canvas
    // this.initCutCanvas()
    // this.initPreviewCanvas()
  }

  /**
   * 绘制
   * 每次交互完成以后都要调用的这个方法
   */
  draw() {
    this.viewImageData = this.countViewImageData()

    const cutFigure = new Figure(this.$DOM.cutCV, this.cutCTX, {
      fillStyle: 'rgba(0,0,0,.5)',
    })
    cutFigure.clear()
    cutFigure.draw('image', this.viewImageData)

    let imageData = this.cutCTX.getImageData(
      this.$DATA.cutData.x,
      this.$DATA.cutData.y,
      this.$DATA.cutData.w,
      this.$DATA.cutData.h
    )
    let preveiwImageData = this.cutCTX.getImageData(
      this.$DATA.cutData.x,
      this.$DATA.cutData.y,
      this.$DATA.cutData.w,
      this.$DATA.cutData.h
    )

    cutFigure.draw('rect', {
      x: 0,
      y: 0,
      w: this.$DOM.cutCV.width,
      h: this.$DOM.cutCV.height,
    })

    const colorDown = 100

    for (let index = 0; index < this.moduleImageData.data.length; index += 4) {
      if (this.moduleImageData.data[index] !== 255) {
        imageData.data[index] = imageData.data[index] - colorDown
        imageData.data[index + 1] = imageData.data[index + 1] - colorDown
        imageData.data[index + 2] = imageData.data[index + 2] - colorDown
        imageData.data[index + 3] = imageData.data[index + 3]

        preveiwImageData.data[index] = 0
        preveiwImageData.data[index + 1] = 0
        preveiwImageData.data[index + 2] = 0
        preveiwImageData.data[index + 3] = 0
      }
    }

    this.cutCTX.putImageData(
      imageData,
      this.$DATA.cutData.x,
      this.$DATA.cutData.y
    )
    this.preveiwCTX.putImageData(preveiwImageData, 0, 0)
    Log._info('this.imageData', this.imageData)
  }

  // 交互总线
  reset() {
    this.countCutData()
    this.initCutCanvas()
    this.initPreviewCanvas()
    this.moduleImageData = this.countModuleImageData()
    this.draw()
  }

  initCutCanvas() {
    const figureData = this.countFigureData(this.$DOM.cutCV)
  }

  initPreviewCanvas() {
    Log._info('this.$DATA.cutData', this.$DATA.cutData)
    this.$DOM.preveiwCV.width = this.$DATA.cutData.w
    this.$DOM.preveiwCV.height = this.$DATA.cutData.h
    this.$DOM.centerCanvasWrap.style.position = 'relative'
    this.$DOM.preveiwCV.style.position = 'absolute'
    this.$DOM.preveiwCV.style.left =
      (this.$DOM.centerCanvasWrap.clientWidth - this.$DATA.cutData.w) / 2 + 'px'
    this.$DOM.preveiwCV.style.top =
      (this.$DOM.centerCanvasWrap.clientHeight - this.$DATA.cutData.h) / 2 +
      'px'
  }

  /**
   * 初始化整体的 dom 结构
   * form 的 dom 结构解耦出去，方便维护
   */
  initElement() {
    Log._info('节点', '初始化页面 dom 节点')
    const CREATE = new Create(this.$wrap)

    // 左|中|右 三个大容器
    this.$DOM.leftWrap = CREATE.leftWrap()
    this.$DOM.centerWrap = CREATE.centerWrap()
    this.$DOM.rightWrap = CREATE.rightWrap()
    this.$wrap.appendChild(this.$DOM.leftWrap)
    this.$wrap.appendChild(this.$DOM.centerWrap)
    this.$wrap.appendChild(this.$DOM.rightWrap)

    // 左边初始化
    this.$DOM.leftCanvasWrap = CREATE.div()
    this.$DOM.leftTools = CREATE.toolsBar()
    this.$DOM.choiceBtn = CREATE.button(
      '选择图片',
      'click',
      this.handleChoice.bind(this)
    )
    this.$DOM.uploadInput = CREATE.uploadInput(
      'change',
      this.handleChange.bind(this)
    )

    const cutCV = CREATE.createCanvas({
      width: this.$DOM.leftWrap.clientWidth,
      height: this.$DOM.leftWrap.clientWidth * 0.75,
      backColor: '#e8f3ff',
      isGPU: true,
    })

    this.$DOM.cutCV = cutCV.cv
    this.cutCTX = cutCV.ctx
    this.$DOM.leftCanvasWrap.appendChild(this.$DOM.cutCV)

    this.$DOM.leftTools.appendChild(this.$DOM.choiceBtn)
    this.$DOM.leftTools.appendChild(this.$DOM.uploadInput)
    this.$DOM.leftWrap.appendChild(this.$DOM.leftCanvasWrap)
    this.$DOM.leftWrap.appendChild(this.$DOM.leftTools)

    this.bindZoom(this.$DOM.cutCV)
    this.bindDrag(this.$DOM.cutCV)

    // 中间初始化
    this.$DOM.centerHeader = CREATE.header('预览裁剪')
    this.$DOM.centerCanvasWrap = CREATE.div({
      height: this.$DOM.leftWrap.clientWidth * 0.75 - 50 + 'px',
    })

    this.$DOM.centerTools = CREATE.toolsBar()
    this.$DOM.downloadBtn = CREATE.button(
      '下载本地',
      'click',
      this.handleDownload.bind(this)
    )
    this.$DOM.uploadBtn = CREATE.button(
      '上传图片',
      'click',
      this.handleUpload.bind(this)
    )

    const preveiwCV = CREATE.createCanvas({
      backColor: '#e8f3ff',
    })
    this.$DOM.preveiwCV = preveiwCV.cv
    this.preveiwCTX = preveiwCV.ctx

    this.$DOM.centerCanvasWrap.appendChild(this.$DOM.preveiwCV)

    this.$DOM.centerTools.appendChild(this.$DOM.uploadBtn)
    this.$DOM.centerTools.appendChild(this.$DOM.downloadBtn)

    this.$DOM.centerWrap.appendChild(this.$DOM.centerHeader)
    this.$DOM.centerWrap.appendChild(this.$DOM.centerCanvasWrap)
    this.$DOM.centerWrap.appendChild(this.$DOM.centerTools)

    // 右边初始化
    this.$DOM.rightHeader = CREATE.header('修改配置')
    this.$DOM.rightForm = CREATE.div({
      height: this.$DOM.leftWrap.clientWidth * 0.75 - 50 + 'px',
    })
    this.$DOM.rightTools = CREATE.toolsBar()

    this.$DOM.resetBtn = CREATE.button(
      '重置',
      'click',
      this.handleReset.bind(this)
    )
    this.$DOM.rightTools.appendChild(this.$DOM.resetBtn)

    this.$DOM.rightWrap.appendChild(this.$DOM.rightHeader)
    this.$DOM.rightWrap.appendChild(this.$DOM.rightForm)
    this.$DOM.rightWrap.appendChild(this.$DOM.rightTools)

    this.setRightForm()
  }

  /**
   * 初始化表单的dom结构
   */
  setRightForm() {
    Log._info('节点', '初始化右侧表单项')
    Log._info('formData', this.$DATA.formData)

    this.$DOM.rightForm.innerHTML = ''

    const CREATE = new Create(this.$wrap)

    const maskTypeItem = CREATE.formItem('裁剪类型')
    const maskType = CREATE.select(
      maskTypeList,
      this.$DATA.formData.type,
      (e) => {
        this.handleChangeFormdata('type', e.target.value)
      }
    )

    maskTypeItem.appendChild(maskType)
    this.$DOM.rightForm.appendChild(maskTypeItem)

    const rotateItem = CREATE.formItem('旋转角度')
    const rotate = CREATE.input(this.$DATA.formData.rotate, (e) => {
      // 过滤非数字
      e.target.value = e.target.value.replace(/[^0-9]/gi, '')

      // 触发数据变更
      this.handleChangeFormdata('rotate', Number(e.target.value))
    })
    const span = CREATE.createDom(
      'span',
      { innerText: '°', className: classMap.rightSpan },
      null
    )
    rotateItem.appendChild(rotate)
    rotateItem.appendChild(span)
    this.$DOM.rightForm.appendChild(rotateItem)

    if (
      this.$DATA.formData.type === 'rect' ||
      this.$DATA.formData.type === 'star'
    ) {
      const zoomItem =
        this.$DATA.formData.type === 'rect'
          ? CREATE.formItem('长宽比')
          : CREATE.formItem('内外径比')
      const zoom = this.$DATA.formData.scale.split(':')
      const zoom1 = CREATE.input(zoom[0], (e) => {
        // 过滤非数字
        e.target.value = e.target.value.replace(/[^0-9.]/gi, '')
        this.handleChangeFormdata('scale', `${e.target.value}:${zoom[1]}`)
      })
      const zoom2 = CREATE.input(zoom[1], (e) => {
        // 过滤非数字
        e.target.value = e.target.value.replace(/[^0-9.]/gi, '')
        this.handleChangeFormdata('scale', `zoom[0]:${e.target.value}`)
      })
      const zoomSpan = CREATE.createDom('span', { innerText: ':' })

      zoomItem.appendChild(zoom1)
      zoomItem.appendChild(zoomSpan)
      zoomItem.appendChild(zoom2)
      this.$DOM.rightForm.appendChild(zoomItem)
    }

    if (
      this.$DATA.formData.type === 'polygon' ||
      this.$DATA.formData.type === 'star'
    ) {
      const pointNumItem =
        this.$DATA.formData.type === 'star'
          ? CREATE.formItem('星数')
          : CREATE.formItem('边数')
      const pointNum = CREATE.input(this.$DATA.formData.point, (e) => {
        e.target.value = e.target.value.replace(/[^0-9]/gi, '')
        this.handleChangeFormdata('point', e.target.value)
      })

      pointNumItem.appendChild(pointNum)
      this.$DOM.rightForm.appendChild(pointNumItem)
    }

    if (this.$DATA.formData.type === 'code') {
      const pointNumItem = CREATE.formItem('文字')
      const pointNum = CREATE.input(this.$DATA.formData.code, (e) => {
        this.handleChangeFormdata('code', e.target.value)
      })

      pointNumItem.appendChild(pointNum)
      this.$DOM.rightForm.appendChild(pointNumItem)

      const fontItem = CREATE.formItem('字体')
      const fontType = CREATE.select(
        fontTypeList,
        this.$DATA.formData.font,
        (e) => {
          this.handleChangeFormdata('font', e.target.value)
        }
      )

      fontItem.appendChild(fontType)
      this.$DOM.rightForm.appendChild(fontItem)
    }
  }

  // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // --[ 计算类型事件 ]------------------------------------|>======>>>>
  // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  /**
   * 计算裁剪区域的外边框属性，可以在进行 imageData 比对的时候优化计算量级
   * @returns 裁剪区域的 x,y,w,h
   */
  countCutData() {
    let cutWidth,
      cutHeight,
      longer = this.$DOM.cutCV.height * this.MASK_SAFE_ZOOM

    // 除了 矩形 之外都是正方形的裁剪区域
    if (this.$DATA.formData.type === 'rect') {
      let [w, h] = this.countScale()

      if (w > h) {
        cutWidth = longer
        cutHeight = longer * (Number(h) / Number(w))
      } else {
        cutWidth = longer * (Number(w) / Number(h))
        cutHeight = longer
      }
    } else {
      cutWidth = cutHeight = longer
    }

    return {
      x: (this.$DOM.cutCV.width - cutWidth) / 2,
      y: (this.$DOM.cutCV.height - cutHeight) / 2,
      w: cutWidth,
      h: cutHeight,
    }
  }

  /**
   * 获取裁剪区域的外边框内部的黑白像素节点
   * @returns imageData
   * 
   * 1- 创建一个和预览区域相等的canvas
   * 2- 在 canvas 上绘制 裁剪形状，用黑白作为区分，白色为保留内容，黑色为切除部分
   * 3- 将绘制结果返回
   */
  countModuleImageData() {
    Log._info('节点', '计算裁剪区域的 imageData')

    // 使用 Create 工具创建  canvas
    const CREATE = new Create(this.$wrap)
    const tempCanvas = CREATE.createCanvas({
      width: this.$DATA.cutData.w,
      height: this.$DATA.cutData.h,
      isGPU: true,
      backColor: '#000',
    })

    // 将 canvas 实例化为一个  Figure 对象
    const ctx = tempCanvas.ctx
    const cv = tempCanvas.cv
    const tempFigure = new Figure(cv, ctx, { fillStyle: '#fff' })

    // 绘制图形
    tempFigure.draw(this.$DATA.formData.type, this.countFigureData(cv))
    
    // 返回 getImageData 图像数据
    return ctx.getImageData(0, 0, cv.width, cv.height)
  }

  /**
   * 计算绘制图形的参数，与 figure.class 去匹配进行绘图
   * @param {*} cv canvas dom 节点
   * @returns 图形参数
   */
  countFigureData(cv) {
    let figure = {}
    let center = {
      x: cv.width / 2,
      y: cv.height / 2,
    }

    console.log(
      '🚀 ~ file: cut-image.class.js:372 ~ CutImage ~ countFigureData ~ this.$DATA.formData.type',
      this.$DATA.formData.type
    )

    if (this.$DATA.formData.type === 'rect') {
      figure = JSON.parse(JSON.stringify(this.$DATA.cutData))
    }

    if (this.$DATA.formData.type === 'code') {
      figure = {
        x: center.x,
        y: center.y,
        size: this.$DOM.cutCV.height * this.MASK_SAFE_ZOOM,
        code: this.$DATA.formData.code,
        font: this.$DATA.formData.font
      }
    }

    if (
      this.$DATA.formData.type === 'circle' ||
      this.$DATA.formData.type === 'love'
    ) {
      figure = {
        x: center.x,
        y: center.y,
        r: (this.$DOM.cutCV.height * this.MASK_SAFE_ZOOM) / 2,
      }
    }

    if (this.$DATA.formData.type === 'star') {
      let [x, y] = this.countScale()
      let _zoom = x > y ? y / x : y / x
      figure = {
        x: center.x,
        y: center.y,
        r1: (this.$DOM.cutCV.height * this.MASK_SAFE_ZOOM) / 2,
        r2: ((this.$DOM.cutCV.height * this.MASK_SAFE_ZOOM) / 2) * _zoom,
        n: this.$DATA.formData.point,
      }
    }

    if (this.$DATA.formData.type === 'polygon') {
      figure = {
        x: center.x,
        y: center.y,
        r: (this.$DOM.cutCV.height * this.MASK_SAFE_ZOOM) / 2,
        n: this.$DATA.formData.point,
      }
    }

    Log._info('figure', figure)
    return figure
  }

  /**
   * 对 表单中 长宽比和内外径比的进行处理
   * 将字符串切割并转换为数字
   * @returns 数字结果
   */
  countScale() {
    let [x, y] = this.$DATA.formData.scale.split(':')

    if (Number(x) === NaN || Number(y) === NaN) {
      Log._err('长宽比的数字不能转化为数字')
      return false
    }

    return [Number(x), Number(y)]
  }

  /**
   * 计算选中图片基于canvas的位置及大小、内容
   * @returns cutCV中图片基于canvas的属性
   * 包含 x,y,w,h,image
   */
  countViewImageData() {
    let _image = {
      x: 0,
      y: 0,
      w: this.image.width * this.ZOOM,
      h: this.image.height * this.ZOOM,
      image: this.image.image,
    }

    Log._info('this.image', this.image)
    Log._info('_image', _image)

    if (this.IS_DRAG) {
      _image.x = this.copyLastViewSize.x + this.$DATA.offsetSize.x
      _image.y = this.copyLastViewSize.y + this.$DATA.offsetSize.y
    } else {
      _image.x =
        this.copyLastViewSize.x + (this.copyLastViewSize.w - _image.w) / 2
      _image.y =
        this.copyLastViewSize.y + (this.copyLastViewSize.h - _image.h) / 2
    }

    Log._info('copyLastViewSize', this.copyLastViewSize)
    Log._info('_image', _image)

    return _image
  }

  // 交互事件
  handleChoice() {
    this.$DOM.uploadInput.click()
  }
  async handleChange(e) {
    Log._info('节点', '图片发生了变化')
    const file = e.target.files[0]
    const image = await promiseGetImage(file)

    let _zoom = 1

    if (image.height >= image.width) {
      _zoom = this.$DOM.cutCV.height / image.height
    } else {
      _zoom = this.$DOM.cutCV.width / image.width
    }
    image.width = image.width * _zoom
    image.height = image.height * _zoom

    image.x =
      image.height >= image.width
        ? (this.$DOM.cutCV.width - image.width) / 2
        : 0
    image.y =
      image.height >= image.width
        ? 0
        : (this.$DOM.cutCV.height - image.height) / 2

    this.image = image

    this.viewImageData = this.copyLastViewSize = {
      x: image.x,
      y: image.y,
      w: image.width,
      h: image.height,
    }

    this.ZOOM = 1
    this.$DATA.offsetSize = { x: 0, y: 0 }

    Log._info('选择图片', image)
    this.draw()
  }
  handleDownload() {
    if (!this.image) {
      Log._e('没有图片，无法下载')
      return false
    }
    const url = this.$DOM.preveiwCV.toDataURL('image/png')
    const oA = document.createElement('a')
    oA.download = '下载' // 设置下载的文件名，默认是'下载'
    oA.href = url
    document.body.appendChild(oA)
    oA.click()
    oA.remove()
  }
  handleUpload() {
    Log._warn('upload 功能开发中...')
  }
  handleReset() {
    Log._warn('reset 功能开发中...')
  }
  handleChangeFormdata(k, v) {
    this.$DATA.formData[k] = v

    if (k === 'type') {
      this.setRightForm()
      // this.countImageData(this.countFigureData(this.$DOM.cutCV))
      this.moduleImageData = this.countModuleImageData()
    }

    this.reset()

    // isReset
    //   ? Log._info('formData.changed.isReset', this.$DATA.formData)
    //   : Log._info('formData.changed', this.$DATA.formData)
  }
  bindZoom(ele) {
    ele.addEventListener(
      'mousewheel',
      function (e) {
        e.preventDefault() // 阻止冒泡

        // 深拷贝数据，避免造成数据污染
        this.copyLastViewSize = JSON.parse(JSON.stringify(this.viewImageData))

        // 根据滚轮滚动方向半段增减，缩放最小值为 0.3
        if (e.wheelDelta > 0) {
          this.ZOOM += this.CONFIG.wheelStep
        } else {
          if (this.ZOOM >= 0.3) {
            this.ZOOM -= this.CONFIG.wheelStep
          }
        }
        this.draw()
      }.bind(this)
    )
  }
  bindDrag(ele) {
    let mouseStart = { x: 0, y: 0 }

    ele.addEventListener(
      'mousedown',
      function (e) {
        e.preventDefault()
        // 判断鼠标是否在图片内部
        if (
          e.offsetX > this.viewImageData.x &&
          e.offsetX < this.viewImageData.x + this.viewImageData.w &&
          e.offsetY > this.viewImageData.y &&
          e.offsetY < this.viewImageData.y + this.viewImageData.h
        ) {
          this.IS_DRAG = true
          ele.style.cursor = 'move'
          this.copyLastViewSize = JSON.parse(JSON.stringify(this.viewImageData))

          // 记录鼠标起始位置
          mouseStart = {
            x: e.offsetX,
            y: e.offsetY,
          }
        }
      }.bind(this)
    )

    // 绑定鼠标松开事件
    ele.addEventListener(
      'mouseup',
      function (e) {
        e.preventDefault()

        this.IS_DRAG = false
        ele.style.cursor = 'default'
      }.bind(this)
    )

    // 绑定鼠标移动事件
    ele.addEventListener(
      'mousemove',
      function (e) {
        e.preventDefault()
        if (this.IS_DRAG) {
          this.$DATA.offsetSize = {
            x: e.offsetX - mouseStart.x,
            y: e.offsetY - mouseStart.y,
          }
          this.draw()
        }
      }.bind(this)
    )
    // 容错机制，当鼠标移出以后，停止拖拽
    // 解决鼠标移出之后的 mouseup 事件无法被监听到
    ele.addEventListener(
      'mouseout',
      function (e) {
        if (this.IS_DRAG) {
          e.preventDefault()

          this.IS_DRAG = false
          ele.style.cursor = 'default'
        }
      }.bind(this)
    )
  }
}

export const createCut = (config) => new CutImage(config)
